/**
 * 
 */
package it.uniroma3.dia.digraph.algorithm.utility.stackedtri;

import it.uniroma3.dia.digraph.algorithm.utility.Utility;

import java.util.Arrays;
import java.util.Comparator;

import org.graphdrawing.graphml.xmlns.NodeType;

/**
 *  
 * @author BarycentricCoords
 *
 */
public class StackedTriNode {
	
	private int depth;
	private int parentNumber;
	private boolean arrived;	
	
	private NodeType node;	
	public double[] lambdas;
	public StackedTriNode[] parents;
	
	public StackedTriNode() {}
	
	public StackedTriNode(NodeType n){
		this.node = n;
		this.arrived = false;
		this.parentNumber = 0;		
		this.depth = Integer.MAX_VALUE;
		
		this.parents = new StackedTriNode[3];
//		this.lambdas = new double[3];
//	
//		this.lambdas[0] = -1;
//		this.lambdas[1] = -1;
//		this.lambdas[2] = -1;
	}
	
	
	public void updatePosition() {
		this.node.setX(this.getParentXCombination());
		this.node.setY(this.getParentYCombination());
		
	}

	
	public void calculateLambda(){
		//calculate
		this.lambdas = Utility.barycentricCoords(this.node, 
				this.parents[0].getNode(), this.parents[1].getNode(), this.parents[2].getNode());
		
		//if this nodes is not strictly IN the triangle formed by its parents...
		if(lambdas[0]>=1 || lambdas[1]>=1 || lambdas[2]>=1 ||
		   lambdas[0]<=0 || lambdas[1]<=0 || lambdas[2]<=0){
			String message = "\n"+this.node.getId()+": wrong convex coordinates \n"+
				parents[0].getId()+lambdas[0]+" "+parents[1].getId()+lambdas[1]+" "+parents[2].getId()+lambdas[2];
			throw new RuntimeException(message);
		}
			
//		this.lambdas = Utility.barycentricCoords(this.node, this.parents.get(0).getNode(), this.parents.get(1).getNode(), this.parents.get(2).getNode());
//		System.out.println(this.getId()+": "+this.parents.get(0).getId()+lambdas[0]+" "+this.parents.get(1).getId()+lambdas[1]+" "+this.parents.get(2).getId()+lambdas[2]);
	}
	
	public double getX(){
//		System.out.println("x req for "+this.getId());
		if(this.depth==0 || this.arrived)
			return node.getX();
		return this.getParentXCombination();
	}

	public double getY(){
//		System.out.println("y req for "+this.getId());
		if(this.depth==0 || this.arrived)
			return node.getY();
		return this.getParentYCombination();
	}

	
	public double getParentXCombination() {
		double result = 0;
//		System.out.println("x comb for "+this.getId()+" d="+this.depth+" pn="+this.parentNumber);
		for (int i=0;i<this.parentNumber;i++){
//			System.out.println(parents[i].getId()+" "+i+" "+parents[i].getX());
//			System.out.println(lambdas[i]);
			result += lambdas[i]*parents[i].getX();
			
		}
		
		return result;
	}
	
	public double getParentYCombination() {
		double result = 0;
//		System.out.println("y comb for "+this.getId());
		for (int i=0;i<3;i++)
			result += lambdas[i]*parents[i].getY();

		return result;
	}
	

	
	
	public String getId(){
		return this.node.getId();
	}
	
	

	
	
	/**
	 * Adds a parent node to this node 
	 * @param parent the parent node to add to this
	 */
	public void addParent(StackedTriNode parent){
		
		this.parents[parentNumber] = parent;
		parentNumber++;
		
	}
	
	/**
	 * @return the labelledNeighbors
	 */
	public int getParentNumber() {
		return parentNumber;
	}

	/**
	 * @return the arrived
	 */
	public boolean isArrived() {
		return arrived;
	}

	/**
	 * @param arrived the arrived to set
	 */
	public void setArrived(boolean arrived) {
		this.arrived = arrived;
	}

	/**
	 * @return the parents
	 */
	public StackedTriNode[] getParents() {
		return parents;
	}


	/**
	 * @return the depth
	 */
	public int getDepth() {
		return depth;
	}

	/**
	 * @param depth the depth to set
	 */
	public void setDepth(int depth) {
		this.depth = depth;
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + (arrived ? 1231 : 1237);
		result = prime * result + depth;
		result = prime * result + Arrays.hashCode(lambdas);
		result = prime * result + ((node == null) ? 0 : node.hashCode());
		result = prime * result + parentNumber;
		result = prime * result + Arrays.hashCode(parents);
		return result;
	}


	/* (non-Javadoc)
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		StackedTriNode other = (StackedTriNode) obj;
		if (arrived != other.arrived)
			return false;
		if (depth != other.depth)
			return false;
		if (!Arrays.equals(lambdas, other.lambdas))
			return false;
		if (node == null) {
			if (other.node != null)
				return false;
		} else if (!node.getId().equals(other.node.getId()))
			return false;
		if (parentNumber != other.parentNumber)
			return false;
		if (!Arrays.equals(parents, other.parents))
			return false;
		return true;
	}


	/**
	 * @param node the node to set
	 */
	public void setNode(NodeType node) {
		this.node = node;
	}

	/**
	 * @return the node
	 */
	public NodeType getNode() {
		return node;
	}

}

class LabelledNeighborsComparator implements Comparator<StackedTriNode>{
	
	@Override
	public int compare(StackedTriNode o1, StackedTriNode o2) {
		return o2.getParentNumber()-o1.getParentNumber();
	}
	
}
